<template>
  <div ref="proTableInst" class="pro-table">
    <custom-card :is-card="styleType === 'one'">
      <table-query-form
        v-if="queryList.length"
        ref="queryFormInst"
        :query="queryList"
        :is-card="styleType === 'two' || styleType === 'three'"
        v-bind="{
          ...props.queryProps,
          ...$attrs
        }"
        :tableTools="props.tableTools"
        @search="handleSearch"
        @reset="resetHandle"
        @collapsedChange="handleCollapsedChange"
        @update:queryForm="(value) => emits('update:queryForm', value)"
      ></table-query-form>
      <custom-card :is-card="styleType === 'two'">
        <custom-card class="pro-table_body" :is-card="styleType === 'three'">
          <table-header
            v-model:scrollX="scrollX"
            :columns="props.columns"
            :tableTitle="tableTitle"
            :isExport="isExport"
            :defaultIndexColume="defaultIndexColume"
            :indexColumeWidth="indexColumeWidth"
            :tableTools="tableTools"
            :isRefresh="isRefresh"
            :isFullscreen="isFullscreen"
            @refresh="handleRefresh"
            @export="handleExport"
            @fullScreenChange="handleFullScreenChange"
            @handleColumns="(columns) => (tableColumns = columns)"
          >
            <template v-if="$slots.tableTitle" #tableTitle>
              <slot name="tableTitle"></slot>
            </template>

            <template v-if="$slots.operation" #operation>
              <slot name="operation"></slot>
            </template>
          </table-header>

          <div
            v-if="$slots.tableTop"
            class="pro-table__top"
            style="margin-bottom: 10px"
          >
            <slot name="tableTop"></slot>
          </div>

          <n-data-table
            ref="dataTableInst"
            v-bind="props"
            :columns="(tableColumns as any)"
            :data="props.data.length ? props.data : tableData"
            :loading="tableLoading"
            :pagination="false"
            :max-height="props.autoHeight ? tableMaxHeight : props.maxHeight"
            :scrollX="scrollX"
          />

          <div
            v-if="$slots.tableBottom"
            class="pro-table__bottom"
            style="margin-top: 10px"
          >
            <slot name="tableBottom"></slot>
          </div>
        </custom-card>
        <table-pagination
          v-if="pagination"
          v-model:page="pageIndex"
          v-model:size="pageSize"
          :is-card="styleType === 'three'"
          :pagination="pagination"
          :total="props.data.length ? props.total : dataTotal"
          :noTotal="noTotal"
          :nextCount="nextCount"
        />
      </custom-card>
    </custom-card>
  </div>
</template>

<script lang="ts" setup>
  import { DataTableInst, NDataTable } from 'naive-ui'
  import { proTableProps } from './props'
  import TableQueryForm from './components/table-query-form/index.vue'
  import TableHeader from './components/table-header/index.vue'
  import TablePagination from './components/table-pagination/index.vue'
  import CustomCard from './components/custom-card/index.vue'
  import { useAutoHeight } from './hooks/useAutoHeight'
  import { useExcelExportDownload } from './hooks/useExcelExportDownload'
  import { useColumnRender } from './hooks/useColumnRender'
  import { useTableScrollToTop } from './hooks/useTableScrollToTop'
  import { useFullscreen } from './hooks/useFullscreen'
  import { debounce } from 'lodash-es'

  const emits = defineEmits<{
    (
      e: 'search',
      params: {
        [index: string]: any
        pageIndex: number
        pageSize: number
      }
    ): void
    (e: 'reset'): void
    (e: 'collapsedChange', value: boolean): void
    (e: 'fullScreenChange', value: boolean): void
    (e: 'refresh'): void
    (e: 'export'): void
    (e: 'pageIndexChange', value: number): void
    (e: 'pageSizeChange', value: number): void
    (e: 'queryParamsChange', value: Record<string, any>): void
    (e: 'update:queryForm', value: object): void
  }>()
  const props = defineProps(proTableProps)

  const proTableInst = ref<HTMLDivElement | null>(null)
  const queryFormInst = ref<InstanceType<typeof TableQueryForm> | null>(null)
  const dataTableInst = ref<DataTableInst | null>(null)

  const tableLoading = ref(false)
  const [pageIndex, resetPageIndex] = useProRef(props.defaultPageIndex)
  const [pageSize, resetPageSize] = useProRef(props.defaultPageSize)
  const [nextCount, resetNextCount] = useProRef(0)
  const [lastId, resetLastId] = useProRef('')
  const [action, resetAction] = useProRef('NEXT')
  const [pageCount, resetPageCount] = useProRef(1)
  const queryParams = ref<Record<string, any>>({})
  const tableData = ref<any[]>([])
  const dataTotal = ref(0)
  const preCount = ref(0)
  const noToralPageParamsRest = ref(false)
  const noToralPageParamsDone = ref(false)

  const tableColumns = ref(props.columns)
  const scrollX = ref<string | number | undefined>(props.scrollX)

  /**
   * 导出功能
   */
  const excelExportDownload = useExcelExportDownload({
    columns: tableColumns,
    data: tableData,
    exportFilename: props.exportFilename
  })

  /**
   * 页码切换自动滚动到最顶层
   */
  useTableScrollToTop({ pageIndex, pageSize, dataTableInst })

  /**
   * 切换满屏
   */
  const { isFullscreen, fullScreenChange } = useFullscreen(proTableInst)

  /**
   * 表格自适应高度
   */
  const { tableMaxHeight, updateTableMaxHeight } = useAutoHeight(
    props,
    queryFormInst,
    isFullscreen
  )

  /**
   * 拦截并改造表格的render函数
   */
  watch(tableColumns, () => {
    useColumnRender({
      columns: tableColumns,
      columnsAlign: props.columnsAlign,
      pageIndex,
      pageSize
    })
  })

  watch(pageIndex, (newValue, oldValue) => {
    action.value = newValue > oldValue ? 'NEXT' : 'PRE'
    if (action.value === 'NEXT') {
      pageCount.value = newValue - oldValue
    } else {
      pageCount.value = oldValue - newValue
    }
    emits('pageIndexChange', pageIndex.value)
    searchEmit()
    requestTableData()
  })

  watch(pageSize, () => {
    emits('pageSizeChange', pageSize.value)
    searchEmit()
    requestTableData()
  })
  const resetPageParams = () => {
    resetPageIndex()
    resetPageSize()
    resetNextCount()
  }

  const queryList = computed(() => {
    if (props.query) return props.query

    const queryList: any[] = []
    props.columns?.forEach((item) => {
      const { query, title, key } = item
      if (query && title && key) {
        queryList.push({
          title: item.title,
          key: item.key,
          dictOptions: item.dictOptions,
          dictCode: item.dictCode,
          valueCode: item.valueCode,
          labelCode: item.labelCode,
          visiable: item.visiable,
          ...query
        })
      }
    })
    return queryList
  })

  const requestTableData = debounce(async (isStatic = false) => {
    try {
      if (props.data?.length || !props.request) return
      if (!isStatic) {
        tableLoading.value = true
      }
      const dataLength = tableData.value.length

      if (noToralPageParamsRest.value) {
        resetLastId()
        resetAction()
        resetPageCount()
      } else if (!noToralPageParamsDone.value && dataLength > 0) {
        lastId.value =
          action.value === 'NEXT'
            ? tableData.value[dataLength - 1].id
            : tableData.value[0].id
      }
      noToralPageParamsRest.value = false
      noToralPageParamsDone.value = false
      const pageParams = props.noTotal
        ? {
            action: action.value,
            lastId: lastId.value,
            pageCount: pageCount.value
          }
        : {
            pageIndex: pageIndex.value,
            pageSize: pageSize.value
          }

      const {
        data = [],
        total = 0,
        preCount: preCount_ = 0,
        nextCount: nextCount_ = 0
      } = await props.request(pageParams, queryParams.value)
      tableData.value = data
      dataTotal.value = total
      preCount.value = preCount_
      nextCount.value = nextCount_
    } catch (e) {
      console.log(e)
    } finally {
      if (!isStatic) {
        tableLoading.value = false
      }
    }
  }, 100)

  const searchEmit = () => {
    emits('search', {
      ...queryParams.value,
      pageIndex: pageIndex.value,
      pageSize: pageSize.value
    })
    emits('queryParamsChange', queryParams.value)
  }

  const handleSearch = (queryForm) => {
    queryParams.value = queryForm
    noToralPageParamsRest.value = true
    resetPageParams()
    requestTableData()
  }

  const handleRefresh = () => {
    noToralPageParamsDone.value = true

    emits('refresh')
    requestTableData()
  }

  const handleExport = () => {
    emits('export')
    excelExportDownload()
  }

  const handleFullScreenChange = () => {
    emits('fullScreenChange', isFullscreen.value)
    fullScreenChange()
  }

  const handleCollapsedChange = (value) => {
    emits('collapsedChange', value)
    updateTableMaxHeight()
  }
  const resetHandle = () => {
    noToralPageParamsRest.value = true
    emits('reset')
  }

  if (!queryList.value.length) {
    requestTableData()
  }

  onActivated(() => {
    if (props.activated) {
      requestTableData()
    }
  })

  defineExpose({
    excelExportDownload,
    reload: () => requestTableData(),
    staticReload: () => requestTableData(true),
    search: (params: Record<string, any>) => {
      queryFormInst.value?.search(params)
    },
    reset: () => {
      queryFormInst.value?.reset()
    },
    noTotalReset: () => {
      resetHandle()
      resetPageParams()
    },
    noTotalSearch: (params: Record<string, any>) => {
      handleSearch(params)
    },
    setPageIndex: (index: number) => {
      pageIndex.value = index
    },
    setPageSize: (size: number) => {
      pageSize.value = size
    },

    // DataTable自带的方法
    clearFilters: () => dataTableInst.value?.clearFilters(),
    clearSorter: () => dataTableInst.value?.clearFilters(),
    filters: (filters) => dataTableInst.value?.filters(filters),
    scrollTo: (options) => dataTableInst.value?.scrollTo(options),
    sort: (columnKey, order) => dataTableInst.value?.sort(columnKey, order),
    page: (page) => dataTableInst.value?.page(page)
  })
</script>

<style lang="less" scoped>
  .pro-table {
    box-sizing: border-box;
    .pro-table_body {
      margin-bottom: 15px;
    }
  }
</style>
